package com.neulogistics.contrib.warehouse.api.application.command.handler;

import com.blackto.lib.mediat.unicast.IRequestHandler;
import com.neulogistics.contrib.warehouse.api.application.command.TransferOrderCommand;
import com.neulogistics.contrib.warehouse.api.application.integrationevent.IWarehouseIntegrationEventService;
import com.neulogistics.contrib.warehouse.api.application.integrationevent.publish.InventoryCheckedIntegrationEvent;
import com.neulogistics.contrib.warehouse.api.service.ThreadLocalService;
import com.neulogistics.contrib.warehouse.domain.exception.WarehouseDomainException;
import com.neulogistics.contrib.warehouse.domain.model.aggregate.Inventory;
import com.neulogistics.contrib.warehouse.domain.model.valueobject.GoodsItem;
import com.neulogistics.contrib.warehouse.domain.repository.IInventoryRepository;
import com.neulogistics.contrib.warehouse.domain.service.IWarehouseDomainService;
import com.neulogistics.infrastructure.cache.idservice.RedisIdUtil;
import com.neulogistics.infrastructure.common.annotation.ProduceEvent;
import com.neulogistics.infrastructure.common.serviceresult.BaseResponse;
import com.neulogistics.infrastructure.common.serviceresult.ResultUtils;
import com.neulogistics.infrastructure.integrationeventlog.model.IntegrationEventConstant;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.Arrays;

@Component
public class TransferOrderCommandHandler implements IRequestHandler<TransferOrderCommand, BaseResponse<Boolean>> {

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private IInventoryRepository inventoryRepository;

    @Resource
    private IWarehouseDomainService warehouseDomainService;

    @Resource
    private IWarehouseIntegrationEventService warehouseIntegrationEventService;

    @Resource
    private RedisIdUtil redisIdUtil;

    @Resource
    private TransactionTemplate transactionTemplate;

    private final String Lock_Name_Prefix;

    private final String CirculationOrder_Id_Key;

    public TransferOrderCommandHandler(@Value("${spring.application.name}") String appName) {
        Lock_Name_Prefix = "lock:" + appName +":TransferOrder:";
        CirculationOrder_Id_Key = appName + ":" + "circulationOrderId";
    }

    @Override
    @ProduceEvent
    public BaseResponse<Boolean> handle(TransferOrderCommand command) throws WarehouseDomainException {
        // 获取库房信息和订单项
        Integer outboundWarehouseId = command.getOutboundWarehouseId();
        Integer inboundWarehouseId = command.getInboundWarehouseId();
        GoodsItem goodsItem = command.getGoodsItems().get(0);
        boolean isAdequate = false;

        // 在对应库房库存上上分布式锁
        String lockName = Lock_Name_Prefix + outboundWarehouseId + ":" + goodsItem.getGoodsId();
        RLock lock = redissonClient.getLock(lockName);
        lock.lock();

        try{
            // 获取库存数据
            Inventory inventory = inventoryRepository.findByWarehouseAndGoods(outboundWarehouseId, goodsItem.getGoodsId());
            // 生成集成事件id并创建事件
            Long eventId = redisIdUtil.nextId(IntegrationEventConstant.Integration_Event_Id_Key);
            InventoryCheckedIntegrationEvent inventoryCheckedIntegrationEvent = new InventoryCheckedIntegrationEvent(eventId);
            BeanUtils.copyProperties(command, inventoryCheckedIntegrationEvent);
            Long transactionId = ThreadLocalService.transactionId.get();

            // 配送
            if(command.getTransferType() == 1){
                boolean checkInventory = inventory.checkInventory(goodsItem.getCount());
                isAdequate = checkInventory;
                // 开启事务
                transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus status) {
                    try {
                        if(checkInventory){
                            inventory.allocateInventory(goodsItem.getCount());

                            // 生成对应出入库单
                            Long outboundOrderId = redisIdUtil.nextId(CirculationOrder_Id_Key);
                            Long inboundOrderId = redisIdUtil.nextId(CirculationOrder_Id_Key);
                            warehouseDomainService.generateCirculationOrderByDelivery(
                                    outboundWarehouseId, outboundOrderId,
                                    inboundWarehouseId, inboundOrderId,
                                    command.getTransferOrderId(), Arrays.asList(goodsItem));
                            inventoryRepository.save(inventory);
                        }
                        inventoryCheckedIntegrationEvent.setIsAdequate(checkInventory);
                        inventoryCheckedIntegrationEvent.setSubWarehouseId(inboundWarehouseId);
                        warehouseIntegrationEventService.addAndSaveEvent(inventoryCheckedIntegrationEvent, transactionId);
                    } catch (Exception e){
                        // 触发回滚
                        throw new RuntimeException(e.getCause().getMessage());
                    }
                    }
                });
            }

            // 退货
            else if (command.getTransferType() == 2){
                // 开启事务
                transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus status) {
                    try {
                        // 生成对应入库单
                        Long subInboundOrderId = redisIdUtil.nextId(CirculationOrder_Id_Key);
                        Long centerInboundOrderId = redisIdUtil.nextId(CirculationOrder_Id_Key);
                        warehouseDomainService.generateCirculationOrderByReturn(inboundWarehouseId, centerInboundOrderId,
                                outboundWarehouseId, subInboundOrderId,
                                command.getTransferOrderId(), Arrays.asList(goodsItem));

                        // 保存集成事件
                        inventoryCheckedIntegrationEvent.setIsAdequate(true);
                        inventoryCheckedIntegrationEvent.setSubWarehouseId(outboundWarehouseId);
                        warehouseIntegrationEventService.addAndSaveEvent(inventoryCheckedIntegrationEvent, transactionId);
                    } catch (Exception e){
                        // 触发回滚
                        throw new RuntimeException(e.getCause().getMessage());
                    }
                    }
                });
            }

            // 换货
            else if (command.getTransferType() == 3){
                boolean checkInventory = inventory.checkInventory(goodsItem.getCount());
                isAdequate = checkInventory;
                // 开启事务
                transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                    @Override
                    protected void doInTransactionWithoutResult(TransactionStatus status) {
                    try {
                        if(checkInventory){
                            inventory.allocateInventory(goodsItem.getCount());

                            // 生成配送环节对应出入库单
                            Long outboundOrderId = redisIdUtil.nextId(CirculationOrder_Id_Key);
                            Long inboundOrderId = redisIdUtil.nextId(CirculationOrder_Id_Key);
                            warehouseDomainService.generateCirculationOrderByDelivery(
                                    outboundWarehouseId, outboundOrderId,
                                    inboundWarehouseId, inboundOrderId,
                                    command.getTransferOrderId(), Arrays.asList(goodsItem));
                            // 生成退货环节对应入库单
                            Long subInboundOrderId = redisIdUtil.nextId(CirculationOrder_Id_Key);
                            Long centerInboundOrderId = redisIdUtil.nextId(CirculationOrder_Id_Key);
                            warehouseDomainService.generateCirculationOrderByReturn(outboundWarehouseId, centerInboundOrderId,
                                    inboundWarehouseId, subInboundOrderId,
                                    command.getTransferOrderId(), Arrays.asList(goodsItem));

                            inventoryRepository.save(inventory);
                        }
                        inventoryCheckedIntegrationEvent.setIsAdequate(checkInventory);
                        inventoryCheckedIntegrationEvent.setSubWarehouseId(inboundWarehouseId);
                        warehouseIntegrationEventService.addAndSaveEvent(inventoryCheckedIntegrationEvent, transactionId);
                    } catch (Exception e){
                        // 触发回滚
                        throw new RuntimeException(e.getCause().getMessage());
                    }
                    }
                });
            }
        } finally {
            // 解锁
            lock.unlock();
        }

        // 返回
        return ResultUtils.success(isAdequate);
    }
}
